#include <string.h>
#include <stdlib.h>

#include "tick.h"
#include "adc.h"
#include "SensorChannels.h"
#include "SensorChannelADC.h"
#include "AnalogTempSensor.h"

#define PT1000_POWER_IO_PORT	GPIOA
#define PT1000_POWER_IO_PIN		GPIO_Pin_7

#define ENABLE_ANALOG_SENSOR_POWER(HANDLE)\
	GPIO_ResetBits(HANDLE->GPIOx, HANDLE->Pin)

#define DISABLE_ANALOG_SENSOR_POWER(HANDLE)\
	GPIO_SetBits(HANDLE->GPIOx, HANDLE->Pin)

#define DEFAULT_READ_PERIOD_TIME_MILLISECOND	100
static int ADCEnable = 0;


typedef struct {
	int isOpen;
	GPIO_TypeDef *GPIOx;
	uint16_t Pin;
	int ReadPeriod;
// 	int AdcChannel;
	unsigned int LastReadTime;
} PT1000_t;

static PT1000_t AnalogTempSensors[PHYSENSOR_CHANNELS] = {0};


static __inline void EnablePullupPower(int channel) {
	GPIO_ResetBits(AnalogTempSensors[channel].GPIOx, AnalogTempSensors[channel].Pin);
}

static __inline void DisablePullupPower(int channel) {
	GPIO_SetBits(AnalogTempSensors[channel].GPIOx, AnalogTempSensors[channel].Pin);
}


/*
** @ Open PT1000 Analog temperature sensor
** @ Reture NULL if NC or Digital sensor is connected
** @ Will block for 1ms
**/
// void *OpenAnalogTemperatureSensor(int AdcChannel, GPIO_TypeDef *PwPort, uint16_t PwPin, int readPeriodInMs) {
int OpenAnalogTemperatureSensor(int channel, const SensorChannelHw_t *Hw, int readPeriodInMs) {
	GPIO_InitTypeDef GPIO_InitStructure;
	PT1000_t *handle;
	uint16_t V[2];
	
	if (ADCEnable == 0) {
		OpenSensorChannelsADC();
	}
	
	ADCEnable ++;
	
// 	handle = malloc(sizeof(PT1000_t));
// 	if(handle == NULL) {
// 		return NULL;
// 	}
	// Enable ADC outside this Driver
	handle = &AnalogTempSensors[channel];
	if(handle->isOpen == TRUE) {
		CloseAnalogTemperatureSensor(channel, Hw);
	}
	
	// Set IOA and IOB as input
	GPIO_InitStructure.GPIO_Pin = Hw->DataLineA.PinMask;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(Hw->DataLineA.Port, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = Hw->DataLineB.PinMask;
	GPIO_Init(Hw->DataLineB.Port, &GPIO_InitStructure);
	
	// Set Pull-up Control as output
	GPIO_InitStructure.GPIO_Pin = Hw->PowerPullupCtrl.PinMask;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(Hw->PowerPullupCtrl.Port, &GPIO_InitStructure);
	
	// Disable 3V3 Power Control
	GPIO_InitStructure.GPIO_Pin = Hw->Power3V3Ctrl.PinMask;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(Hw->Power3V3Ctrl.Port, &GPIO_InitStructure);
	
	// Disable 5V Power Control
	if(Hw->Power5VCtrl.Port) {
		GPIO_InitStructure.GPIO_Pin = Hw->Power5VCtrl.PinMask;
		GPIO_Init(Hw->Power5VCtrl.Port, &GPIO_InitStructure);
	}
	
	handle->GPIOx = Hw->PowerPullupCtrl.Port;
	handle->Pin   = Hw->PowerPullupCtrl.PinMask;
	handle->ReadPeriod = readPeriodInMs;
	// handle->AdcChannel = AdcChannel;
	if (handle->ReadPeriod == -1) {
		handle->ReadPeriod = DEFAULT_READ_PERIOD_TIME_MILLISECOND;
	}
	handle->LastReadTime = get_tick_count();
	// PowerOn(handle);
	EnablePullupPower(channel);
	// Must delay for 2 milliseconds for Volt statble
	delay_ms(2);
	
	// Read ADC Value for Analog Channel
	// ADC_SoftwareStart(ADC1);
	// ADC_Read(handle->AdcChannel, &V[0], &V[1]);
	ReadSensorChannelsADC(channel, &V[0], &V[1]);
	
	// PowerOff(handle);
	DisablePullupPower(channel);
	
	// That means no PT1000 connected
	if (abs(V[0] - V[1]) < 200) {
		// free(handle);
		handle->isOpen = FALSE;
		return -1;
	}
	
	handle->isOpen = TRUE;
	return 0;
}

/*
** @ Close the temperature sensor
**/
void CloseAnalogTemperatureSensor(int channel, const SensorChannelHw_t *Hw) {
// 	if(handle == NULL)
// 		return;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	if(AnalogTempSensors[channel].isOpen == FALSE) {
		return;
	}
	// DisablePullupPower(channel);
	
	// Config the Pull-up Control as input, this will disable the Pull-up
	GPIO_InitStructure.GPIO_Pin = Hw->PowerPullupCtrl.PinMask;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(Hw->PowerPullupCtrl.Port, &GPIO_InitStructure);

	// PowerOff(handle);
	// free(handle);
	ADCEnable --;
	if (ADCEnable == 0) {
		CloseSensorChannelsADC();
	}
	AnalogTempSensors[channel].isOpen = FALSE;
}

/*
** @ Read success, return 1, else 0
**/
int ReadAnalogTemperatureSensor(int channel, uint16_t *V1, uint16_t *V2) {
	if(AnalogTempSensors[channel].isOpen == FALSE) 
		return 0;
	
	if ( (get_tick_count() - AnalogTempSensors[channel].LastReadTime) > AnalogTempSensors[channel].ReadPeriod ) {
		EnablePullupPower(channel);
		delay_ms(2);
		ReadSensorChannelsADC(channel, V1, V2);
		DisablePullupPower(channel);
		AnalogTempSensors[channel].LastReadTime = get_tick_count();
		return 1;
	} else {
		return 0;
	}		
}

/*
** @ Set read period in millisecond
**/
int SetAnalogTemperatureSensorReadPeriodInMs(int channel, int MilliSecond) {
// 	if(handle == NULL) 
// 		return -1;
	if(AnalogTempSensors[channel].isOpen == FALSE) 
		return 0;
	
	if(MilliSecond < 0) {
		MilliSecond = DEFAULT_READ_PERIOD_TIME_MILLISECOND;
	}
	AnalogTempSensors[channel].ReadPeriod = MilliSecond;
	return 0;
}

/* TEST ONLY TEST ONLY TEST ONLY */
void AnalogTempSensorMain() {
	uint16_t V1, V2;
	int ret;
	int channel = 1;
	
	ret = OpenAnalogTemperatureSensor(channel, &channelHwInfo[channel], -1);

	if(ret == 0) {
		while (1) {
			if (ReadAnalogTemperatureSensor(channel, &V1, &V2)) {
				V1 = V1;
				V2 = V2;
			}
		}
	}
}
